package ma.jeetconsulting.pocs.dependency.util;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.settings.IvySettings;
import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
import org.apache.ivy.plugins.resolver.URLResolver;
import org.apache.maven.model.Dependency;

import exceptions.ClassNameError;
import exceptions.UnresolvedDependency;

public class JARReader {

	private static final String PACKAGE_INFO_CLASS = "package-info.class";
	private static final String TMP_FILE_NAME = "ivy";
	private static final String TMP_FILE_EXTENSION = ".xml";
	private static final String REPOSITORY_NAME = "central";
	private static final String REPOSITORY_URL_PATTERN = "http://repo1.maven.org/maven2/[organisation]/[module]/[revision]/[artifact](-[revision]).[ext]";
	private static final String MODULE_REVISION_NAME = "working";
	private static final String DEPENDENCY_ARTIFACT_SUFFIX = "-caller";
	private static final String CLASS_SUFFIX = ".class";

	public JarFile getJarFile(Dependency dependency) throws IOException, ParseException {
		
		IvySettings ivySettings = createIvySettingsAndUrlResolver();
		
        Ivy ivy = Ivy.newInstance(ivySettings);

        File ivyfile = createIvyTmpFile();

        DefaultModuleDescriptor md = createTheDefaultModuleDescriptor(dependency);
        
        XmlModuleDescriptorWriter.write(md, ivyfile);

        File jarArtifactFile = createTheJarArtifactFile(ivy, ivyfile);
        
        return new JarFile(jarArtifactFile);
	}

	@SuppressWarnings("deprecation")
	private File createTheJarArtifactFile(Ivy ivy, File ivyfile) {
		String[] confs = new String[]{"default"};
        ResolveOptions resolveOptions = new ResolveOptions().setConfs(confs);
        
        //init resolve report
		ResolveReport report = null;
		try {
			report = ivy.resolve(ivyfile.toURL(), resolveOptions);
		} catch ( ParseException | IOException e) {
			e.printStackTrace();
		}
        
		if (report.getArtifacts().isEmpty()){
			throw new UnresolvedDependency();
		}
		
        //so you can get the jar library
        File jarArtifactFile = report.getAllArtifactsReports()[0].getLocalFile();
		return jarArtifactFile;
	}

	private DefaultModuleDescriptor createTheDefaultModuleDescriptor(
			Dependency dependency) {
		String[] dep = null;
        dep = new String[]{dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()};
        
        DefaultModuleDescriptor md =
                DefaultModuleDescriptor.newDefaultInstance(ModuleRevisionId.newInstance(dep[0],
                dep[1] + DEPENDENCY_ARTIFACT_SUFFIX, MODULE_REVISION_NAME));

        DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md,
                ModuleRevisionId.newInstance(dep[0], dep[1], dep[2]), false, false, true);
        md.addDependency(dd);
		return md;
	}

	private File createIvyTmpFile() throws IOException {
		File ivyfile = File.createTempFile(TMP_FILE_NAME, TMP_FILE_EXTENSION);
        ivyfile.deleteOnExit();
		return ivyfile;
	}

	private IvySettings createIvySettingsAndUrlResolver() {
        IvySettings ivySettings = new IvySettings();
        URLResolver resolver = new URLResolver();
        resolver.setM2compatible(true);
        resolver.setName(REPOSITORY_NAME);
        resolver.addArtifactPattern(REPOSITORY_URL_PATTERN);
        ivySettings.addResolver(resolver);
        ivySettings.setDefaultResolver(resolver.getName());
		return ivySettings;
	}

	public List<String> getPackages(JarFile jarFile) {
		
		List<String> packages = new ArrayList<>();
		Enumeration<JarEntry> jarEntries = jarFile.entries();
		
		while (jarEntries.hasMoreElements()){
			JarEntry jarEntry = jarEntries.nextElement();
			
			if (jarEntry.getName().contains(CLASS_SUFFIX) && !jarEntry.getName().contains(PACKAGE_INFO_CLASS)){
				if (getIndexOfTheFirstCapitalLetter(jarEntry.getName()) == -1){
					throw new ClassNameError();
				}
				String packageName = jarEntry.getName().substring(0, getIndexOfTheFirstCapitalLetter(jarEntry.getName())-1);
				packageName = packageName.replaceAll("/", ".");
				if (!packages.contains(packageName)){
					packages.add(packageName);
				}
			}
		}
		return packages;
	}

	private int getIndexOfTheFirstCapitalLetter(String packageName) {
		Pattern pattern = Pattern.compile("[A-Z]");
		Matcher matcher = pattern.matcher(packageName);
		if (matcher.find()){
			return matcher.start();
		}else{
			return -1;
		}
	}
}
